import 'package:flutter/material.dart';
import 'package:xsdk_flutter/xsdk/framework/base/xsdk_function.dart';
import 'package:xsdk_flutter/xsdk/framework/bean/x_bean_data_table.dart';
import 'package:xsdk_flutter/xsdk/framework/bean/x_bean_menu.dart';
import 'package:xsdk_flutter/xsdk/operation/x_constant_temp.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_color.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_dart.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_log.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_string.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_toast.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_widget.dart';


/*
 通用StatefulWidget父类,与XState联合使用,只需自定义XState即可
 XStatefulWidget(XState());
 用于控件
 */
class XStatefulWidget<T extends State> extends StatefulWidget {

  //来自上一页面的方法(用于: 在此页面返回上一界面时调用,通知响应返回值)
  XFunctionResultCallback<dynamic>? resultCallback;
  //用于监听界面绘制完成后调用updateData()更新数据方法,保证同一XStatefulWidget实例,只执行一次;
  bool isUpdateDataOnce=false;

  //上个界面传入参数
  String? xTag;
  dynamic xData;

  /*
  注意:用于state的数据保存(同一XStatefulWidget实例,状态值不变)
  解决其他界面使用state机制(wantKeepAlive为true)无法保存状态值
  widget.xStateValue1=XXX;
  widget.listData=XXX;
   */

  //用于listview,gridview
  List<dynamic> listData=[];
  List<dynamic> listData2=[];
  //用于DataTable,列标签标题
  List<XBeanDataTableLabel> dataTableColumnsLabels=[];
  //tabbar
  List<XBeanMenuItem> listTabItems=[];//注意:item不能为null

  //记录listview初始化位置
  double currentListViewPosition=0.0;//left listview
  double currentListViewPosition2=0.0;//right listview
  //记录scrollview初始化位置
  double currentScrollViewPosition=0.0;

  //分页加载
  int currentPageIndex=1;
  int totalPages=1;

  //tabView使用
  int currentTabIndex=0;
  XBeanMenuItem? currentTabItem;

  //内置BottomNavBar使用
  int currentBottomNavBarIndex=0;

  //注意:不能在state的构造方法中赋值,会报错,这时page还没依赖完成为空,要在oncreate中赋值
  dynamic xStateValue1;
  dynamic xStateValue2;
  dynamic xStateValue3;
  dynamic xStateValue4;
  dynamic xStateValue5;
  dynamic xStateValue6;
  dynamic xStateValue7;
  dynamic xStateValue8;
  dynamic xStateValue9;
  //多层则使用map
  dynamic xTempValue1;
  dynamic xTempValue2;
  dynamic xTempValue3;
  dynamic xTempValue4;
  dynamic xTempValue5;
  dynamic xTempValue6;
  dynamic xTempValue7;
  dynamic xTempValue8;
  dynamic xTempValue9;

  /*
  官方是一个State对应要一个StatefulWidget,为了避免重复写多个StatefulWidget,这里实现State共用统一个StatefulWidget
   */

  //注意:这种在构造函数中传入State会在返回切换路由时报错(TabBar/BottomNavigationBar),因为State已经被销毁,但是StatefulWidget不会被销毁,导致返回路由,再次获得State为null值
  //另外:dart支持反射,但flutter禁用发射,也不能通过类Type创建实例方式实现
  // T xState;
  // XStatefulWidget(this.xState, {super.key});
  // @override
  // T createState() {
  //   return xState;
  // }

  //ok通过传入方法function方式实现共用同一个StatefulWidget,就算state销毁了,StatefulWidget重新调用createState()方法,实际调用的外部实现传入的ValueGetter方法,达到创建新实例;
  ValueGetter<T> createStateFunction;

  //内部的state,createStateFunction()
  dynamic _xState;

  XStatefulWidget(
      this.createStateFunction,
      {
        super.key,//通过GlobalKey通知更新内部
        this.resultCallback,
        this.xTag,
        this.xData
      }
      );

  @override
  T createState() {
    _xState=createStateFunction();
    return _xState;
  }

  //外界通过XStatefulWidget通知其内部的state执行
  void notifyXStateMsg({dynamic msg}){
    (_xState as XState).receiveXStatefulWidgetMsg(msg);
  }

  //内部State的实际类名
  String getXStateName(){
    //注意:实测不能用_xState实例替代createStateFunction(),外界page的toString调用此时的_xState为空
    return XDartUtils.getRuntimeTypeName(createStateFunction());
  }


}

//使用State要嵌套XStatefulWidget中/////////////////////////////////

/*
 所有XState父类
 State生命周期: 构造方法 -> initState -> didChangeDependencies -> build -> deactivate -> dispose
 */
abstract class XState<T extends XStatefulWidget> extends State<T> with AutomaticKeepAliveClientMixin {

  String? xTag;

  /*
  实现with AutomaticKeepAliveClientMixin 保存页面状态(使用TabBar和BottomNavigationBar)
  注意: 在build()中必须调用super.build(context);
   */
  //是否保留TabBar和BottomNavigationBar的子界面
  bool isKeepPageState=false;
  //重写AutomaticKeepAliveClientMixin的wantKeepAlive( bool get wantKeepAlive => true;)
  @override
  bool get wantKeepAlive => isKeepPageState;


  //子类取消required,(这里强制required则子类会提示生成构造方法)
  XState(
      {
        //值true时:TabBar/BottomNavigationBar切换时,保留上次页面状态,实测:其他界面移除子widget实例变量,重新加载还是不能保存状态值;
        this.isKeepPageState=false,
        this.xTag,
      }
      );


  /*
  1.生命周期,初始化,只会执行一次,相当于onCreate()方法
  注意:重写该方法,要加入super.initState();实现父类的api
  在此方法中,可以配置动画,订阅platform services,controller等,通常用于初始化数据
  //注意:不要在此获取数据,否则报错,因此此时flutter框架层还没有把context与state绑定关联在一起,因此不能访问context,
  如:
  final Map<String,dynamic> arguments = ModalRoute.of(context)?.settings.arguments as Map<String,dynamic>;
  double width=XFlutterUtils.getScreen();
   */
  @override
  void initState() {
    super.initState();
    //初始化数据(只执行一次)
    //XLogUtils.printLog('---initState: isKeepPageState= $isKeepPageState,widget.isUpdateDataOnce=${widget.isUpdateDataOnce}');

    //在界面绘制完成后调用,每次注册,只执行一次,完成后会自动移除(后期其他交互需重新注册使用), (避免报错: setState() or markNeedsBuild() called during build)
    WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {
      /*
      获得数据,保证第一次加载完界面通知执行更新数据(只执行一次);
      实测:设置isKeepPageState=true,在其他界面移除子widget实例变量,重新加载原来的widget实例变量还是不能保存状态值;
      因此为了保证加载一次和在XPageCustomBottomNavigationBarState中使用保存状态值,把数据改为保存在XPage中即可解决;
       */
      if(!widget.isUpdateDataOnce){
        widget.isUpdateDataOnce=true;
        if(getIsPageIndexStartWith1()){
          updateData(pageIndex: 1);
        }else{
          updateData(pageIndex: 0);
        }

        updateData2();
      }else{
        onReCreateFinishShowUI();
      }

      //XLogUtils.printLog('----新建state,addPostFrameCallback 通知绘制完成,只通知一次');

    });

    //每帧都回调(每个widget都回调,无意义)
    // WidgetsBinding.instance.addPersistentFrameCallback((timeStamp) {
    //   XLogUtils.printLog('----addPersistentFrameCallback 每帧都回调?');
    // });

    onCreate();
  }


  /*
  注意:与pagestate的onResumed不一样,这里某情景重新触发新建state绘制完成都会通知
   */
  void onReCreateFinishShowUI(){

  }


  //由于flutter没有直接的setState完成通知,使用Future.delayed方式,适用滑动listview到末尾
  setStateWithFinishCallback(
      VoidCallback fn,
      {
        int delayedMilliseconds=100,
        void Function()? onFinishCallback,
      }){
    //
    setState(fn);
    //
    if(onFinishCallback!=null){
      //延迟执行，假设 widget 重新构建和布局完成大约需要 100 毫秒
      Future.delayed(
          Duration(milliseconds: delayedMilliseconds),
              (){
            XLogUtils.printLog('---setState,onFinishCallback');
            onFinishCallback();
          }
      );
    }

  }


  @required
  void onCreate();

  /*
  2.生命周期,相当于onStart,在initState()之后执行,
  注意:重写该方法,要加入super.didChangeDependencies();实现父类的api
  在这里可以跨组件获得数据,可以访问context:
  调用BuildContext.inheritFromWidgetOfExactType()获得InheritedWidget(State中的子InheritedWidget)。
  获得上一界面传递的数据:
  final Map<String,dynamic> arguments = ModalRoute.of(context)?.settings.arguments as Map<String,dynamic>;

  注意:调用setState后不会触发此方法

  专门用来处理 State 对象依赖关系变化: State 对象的依赖关系发生变化后，Flutter 会回调该方法，随后触发组件构建。
  State 对象依赖关系发生变化的典型场景：系统语言 Locale 或应用主题改变时，弹出dialog
  系统会通知 State 执行 didChangeDependencies 回调方法

   */
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //
    //XLogUtils.printLog('---onStart');
    onStart();

  }
  /*
  用于初始化获得数据
  注意:如果初始化获得数据是异步的,则需要重写该方法时添加async,并向await执行防止最后,否则会导致await后的方法执行可能慢与onBuild执行
   */
  @required
  void onStart();

  /*
  界面加载完成更新数据
  注意:某些场景更新页面数据或触发其他事件时可结合if (mounted) {}判断使用
   */
  @required
  Future<void> updateData({int pageIndex=1});

  //界面加载完成更新数据2
  void updateData2(){}

  //当组件发生变化时会调用,父节点重构widget也会调用,即热重载会调用,但setState()方法并不会调用;
  // @override
  // void didUpdateWidget(covariant T oldWidget) {
  //   super.didUpdateWidget(oldWidget);
  // }

  /*
  3.生命周期:build显示界面
  注意:每次setState()会触发执行,从而对UI的更新(因此数据初始化或获得数据方法都不能放在这里)
   */
  @override
  Widget build(BuildContext context) {
    //实现with AutomaticKeepAliveClientMixin,当为true时必须调用super.build(context)
    super.build(context);

    //每次刷新ui触发需要重建的子widget
    rebuildItemWidget();

    return onBuild(context);
  }

  //用于创建UI界面
  @required
  Widget onBuild(BuildContext context);

  /*
  4.生命周期:停止,相当于onStop,在dispose()之前执行
  重写该方法,要添加super.deactivate();实现父类的api

  注意:实测切换page不一定都会调用此方法??

  当组件的可见状态发生变化时，deactivate 方法会被调用，这时 State 会被暂时从视图树中移除。
  注意：页面切换时，由于 State 对象在视图树中的位置发生了变化，需要先暂时移除后再重新添加，重新触发组件构建，因此也会调用 deactivate 方法
   */

  /*
  5.生命周期:最后结束,相当于onDestroy,在deactivate()之后执行
  注意:重写该方法,要添加super.dispose();实现父类的api

  当 State 被永久地从视图树中移除时，Flutter 会调用 dispose 方法，而一旦 dispose 方法被调用，组件就要被销毁了，
  因此可以在 dispose 方法中进行最终的资源释放、移除监听、清理环境等工作
   */
  @override
  void dispose() {
    onDestroy();
    hideLoading();
    super.dispose();
  }

  //销毁
  @required
  void onDestroy();

  /*
  生命周期,组件的状态改变的时候就会调用didUpdateWidget, 当Widget 的配置发生变化时，或热重载时，系统会回调该方法
  实际上这里flutter框架会创建一个新的Widget,绑定本State，并在这个函数中传递老的Widget。
  这个函数一般用于比较新、老Widget，看看哪些属性改变了，并对State做一些调整。
  需要注意的是，涉及到controller的变更，需要在这个函数中移除老的controller的监听，并创建新controller的监听。
  注意:调用setState后不会触发此方法
   */
  // @override
  // void didUpdateWidget(XStatefulWidget oldPage) {
  //   super.didUpdateWidget(oldPage);
  // }

//////////////////////////////////////////////////////

  //分页加载数据是否从第1页开始, 否则从0开始
  bool getIsPageIndexStartWith1(){
    return true;
  }

  //每次刷新ui出发
  void rebuildItemWidget(){

  }

  //外界通过XStatefulWidget通知其内部state执行
  void receiveXStatefulWidgetMsg(dynamic msg){}

  //Loading////////////////////////////////////////////////////
  /*
  更多: https://pub-web.flutter-io.cn/packages/loading_animation_widget
   */

  OverlayEntry? _loadingOverlayEntry;

  void showLoading(
      {
        bool isOurSideCanClick=true,
        Color? indicatorColor,
        String? text,
        TextStyle? textStyle,
      }
      ){

    try {

      if(_loadingOverlayEntry!=null){
        return;
      }

      _loadingOverlayEntry = OverlayEntry(
        builder: (_) {
          return IgnorePointer(
              ignoring:isOurSideCanClick,
              child: XWidgetUtils.getLayoutContainer(
                backgroundColor: Colors.transparent,
                child: XWidgetUtils.getLayoutColumn(
                    mainAxisAlignment:MainAxisAlignment.center,
                    children: [
                      XWidgetUtils.getLayoutCenter(
                          SizedBox(
                              width: XTempData.defaultLoadingWidgetWidthHeight,
                              height: XTempData.defaultLoadingWidgetWidthHeight,
                              child:CircularProgressIndicator(
                                strokeWidth: XTempData.defaultLoadingWidgetStrokeWidth,
                                color: indicatorColor??XColorUtils.appMainColor,
                                // backgroundColor: Colors.blue,
                                // valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
                              )
                          )
                      ),
                      XStringUtils.isEmpty(text)?XWidgetUtils.getLayoutContainer():
                      XWidgetUtils.getLayoutCenter(
                          XWidgetUtils.getWidgetText(
                            text!,
                            style: textStyle,
                          )
                      ),
                    ]
                ),
              )
          );
        },
      );

      //
      Overlay.of(context).insert(_loadingOverlayEntry!);

    }catch (e) {
      XLogUtils.printLog('---showLoading报错:${e.toString()}');
    }

  }
  void hideLoading(){
    try {
      _loadingOverlayEntry?.remove();
      _loadingOverlayEntry = null;
    }catch(e){
      //
    }
  }


  void toast(String msg,{int? maxLength=100}){
    XToastUtils.toast(context: context, msg: msg,maxLength:maxLength);
  }

}